package com.gitee.wsl.text.parameter


/**
 * Provides data structure for associating a [String] with a [List] of Strings
 */
 interface StringValues {
     companion object {
        /**
         * Empty [StringValues] instance
         */
         val Empty: StringValues = StringValuesImpl()

        /**
         * Builds a [StringValues] instance with the given [builder] function
         * @param caseInsensitiveName specifies if map should have case-sensitive or case-insensitive names
         * @param builder specifies a function to build a map
         */
         inline fun build(
            caseInsensitiveName: Boolean = false,
            builder: StringValuesBuilder.() -> Unit
        ): StringValues = StringValuesBuilderImpl(caseInsensitiveName).apply(builder).build()
    }

    /**
     * Specifies if map has case-sensitive or case-insensitive names
     */
     val caseInsensitiveName: Boolean

    /**
     * Gets first value from the list of values associated with a [name], or null if the name is not present
     */
     operator fun get(name: String): String? = getAll(name)?.firstOrNull()

    /**
     * Gets all values associated with the [name], or null if the name is not present
     */
     fun getAll(name: String): List<String>?

    /**
     * Gets all names from the map
     */
     fun names(): Set<String>

    /**
     * Gets all entries from the map
     */
     fun entries(): Set<Map.Entry<String, List<String>>>

    /**
     * Checks if the given [name] exists in the map
     */
     operator fun contains(name: String): Boolean = getAll(name) != null

    /**
     * Checks if the given [name] and [value] pair exists in the map
     */
     fun contains(name: String, value: String): Boolean = getAll(name)?.contains(value) ?: false

    /**
     * Iterates over all entries in this map and calls [body] for each pair
     *
     * Can be optimized in implementations
     */
     fun forEach(body: (String, List<String>) -> Unit): Unit = entries().forEach { (k, v) -> body(k, v) }

    /**
     * Checks if this map is empty
     */
     fun isEmpty(): Boolean
}

@Suppress("KDocMissingDocumentation")
 open class StringValuesImpl(
    final override val caseInsensitiveName: Boolean = false,
    values: Map<String, List<String>> = emptyMap()
) : StringValues {

    protected val values: Map<String, List<String>>

    init {
        val newMap: MutableMap<String, List<String>> = if (caseInsensitiveName) caseInsensitiveMap() else mutableMapOf()
        values.forEach { (key, value) -> newMap[key] = List(value.size) { value[it] } }
        this.values = newMap
    }

    override operator fun get(name: String): String? = listForKey(name)?.firstOrNull()

    override fun getAll(name: String): List<String>? = listForKey(name)

    override operator fun contains(name: String): Boolean = listForKey(name) != null

    override fun contains(name: String, value: String): Boolean = listForKey(name)?.contains(value) ?: false

    override fun names(): Set<String> = values.keys

    override fun isEmpty(): Boolean = values.isEmpty()

    override fun entries(): Set<Map.Entry<String, List<String>>> = values.entries

    override fun forEach(body: (String, List<String>) -> Unit) {
        for ((key, value) in values) body(key, value)
    }

    private fun listForKey(name: String): List<String>? = values[name]

    override fun toString(): String = "StringValues(case=${!caseInsensitiveName}) ${entries()}"

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is StringValues) return false
        if (caseInsensitiveName != other.caseInsensitiveName) return false
        return entriesEquals(entries(), other.entries())
    }

    override fun hashCode(): Int = entriesHashCode(entries(), 31 * caseInsensitiveName.hashCode())
}

/**
 * Build an instance of [StringValues] from a vararg list of pairs
 */
 fun valuesOf(vararg pairs: Pair<String, List<String>>, caseInsensitiveKey: Boolean = false): StringValues {
    return StringValuesImpl(caseInsensitiveKey, pairs.asList().toMap())
}

/**
 * Build an instance of [StringValues] from a single pair
 */
 fun valuesOf(name: String, value: String, caseInsensitiveKey: Boolean = false): StringValues {
    return StringValuesSingleImpl(caseInsensitiveKey, name, listOf(value))
}

/**
 * Build an instance of [StringValues] with a single [name] and multiple [values]
 */
 fun valuesOf(name: String, values: List<String>, caseInsensitiveKey: Boolean = false): StringValues {
    return StringValuesSingleImpl(caseInsensitiveKey, name, values)
}

/**
 * Build an empty [StringValues] instance.
 */
 fun valuesOf(): StringValues = StringValues.Empty

/**
 * Build an instance of [StringValues] from the specified [map]
 */
 fun valuesOf(map: Map<String, Iterable<String>>, caseInsensitiveKey: Boolean = false): StringValues {
    val size = map.size
    if (size == 1) {
        val entry = map.entries.single()
        return StringValuesSingleImpl(caseInsensitiveKey, entry.key, entry.value.toList())
    }
    val values: MutableMap<String, List<String>> =
        if (caseInsensitiveKey) caseInsensitiveMap() else LinkedHashMap(size)
    map.entries.forEach { values.put(it.key, it.value.toList()) }
    return StringValuesImpl(caseInsensitiveKey, values)
}

/**
 * Copy values to a new independent map
 */
 fun StringValues.toMap(): Map<String, List<String>> =
    entries().associateByTo(LinkedHashMap(), { it.key }, { it.value.toList() })

/**
 * Copy values to a list of pairs
 */
 fun StringValues.flattenEntries(): List<Pair<String, String>> = entries().flatMap { e ->
    e.value.map { e.key to it }
}

/**
 * Invoke [block] function for every value pair
 */
 fun StringValues.flattenForEach(block: (String, String) -> Unit): Unit = forEach { name, items ->
    items.forEach { block(name, it) }
}

/**
 * Create a new instance of [StringValues] filtered by the specified [predicate]
 * @param keepEmpty when `true` will keep empty lists otherwise keys with no values will be discarded
 */
 fun StringValues.filter(keepEmpty: Boolean = false, predicate: (String, String) -> Boolean): StringValues {
    val entries = entries()
    val values: MutableMap<String, MutableList<String>> =
        if (caseInsensitiveName) caseInsensitiveMap() else LinkedHashMap(entries.size)

    entries.forEach { entry ->
        val list = entry.value.filterTo(ArrayList(entry.value.size)) { predicate(entry.key, it) }
        if (keepEmpty || list.isNotEmpty()) {
            values.put(entry.key, list)
        }
    }

    return StringValuesImpl(caseInsensitiveName, values)
}

internal fun entriesEquals(a: Set<Map.Entry<String, List<String>>>, b: Set<Map.Entry<String, List<String>>>): Boolean {
    return a == b
}

internal fun entriesHashCode(entries: Set<Map.Entry<String, List<String>>>, seed: Int): Int {
    return seed * 31 + entries.hashCode()
}

/**
 * Create an instance of case-insensitive mutable map. For internal use only.
 */
 fun <Value : Any> caseInsensitiveMap(): MutableMap<String, Value> = CaseInsensitiveMap()


/**
 * A map with case-insensitive [String] keys
 */
 class CaseInsensitiveMap<Value : Any> : MutableMap<String, Value> {
    private val delegate = mutableMapOf<CaseInsensitiveString, Value>()

    override val size: Int get() = delegate.size

    override fun containsKey(key: String): Boolean = delegate.containsKey(CaseInsensitiveString(key))

    override fun containsValue(value: Value): Boolean = delegate.containsValue(value)

    override fun get(key: String): Value? = delegate[key.caseInsensitive()]

    override fun isEmpty(): Boolean = delegate.isEmpty()

    override fun clear() {
        delegate.clear()
    }

    override fun put(key: String, value: Value): Value? = delegate.put(key.caseInsensitive(), value)

    override fun putAll(from: Map<out String, Value>) {
        from.forEach { (key, value) -> put(key, value) }
    }

    override fun remove(key: String): Value? = delegate.remove(key.caseInsensitive())

    override val keys: MutableSet<String>
        get() = DelegatingMutableSet(
            delegate.keys,
            { content },
            { caseInsensitive() }
        )

    override val entries: MutableSet<MutableMap.MutableEntry<String, Value>>
        get() = DelegatingMutableSet(
            delegate.entries,
            { Entry(key.content, value) },
            { Entry(key.caseInsensitive(), value) }
        )

    override val values: MutableCollection<Value> get() = delegate.values

    override fun equals(other: Any?): Boolean {
        if (other == null || other !is CaseInsensitiveMap<*>) return false
        return other.delegate == delegate
    }

    override fun hashCode(): Int = delegate.hashCode()
}

private class Entry<Key, Value>(
    override val key: Key,
    override var value: Value
) : MutableMap.MutableEntry<Key, Value> {

    override fun setValue(newValue: Value): Value {
        value = newValue
        return value
    }

    override fun hashCode(): Int = 17 * 31 + key!!.hashCode() + value!!.hashCode()

    override fun equals(other: Any?): Boolean {
        if (other == null || other !is Map.Entry<*, *>) return false
        return other.key == key && other.value == value
    }

    override fun toString(): String = "$key=$value"
}

internal fun String.caseInsensitive(): CaseInsensitiveString = CaseInsensitiveString(this)

internal class CaseInsensitiveString(val content: String) {
    private val hash = content.lowercase().hashCode()

    override fun equals(other: Any?): Boolean =
        (other as? CaseInsensitiveString)?.content?.equals(content, ignoreCase = true) == true

    override fun hashCode(): Int = hash

    override fun toString(): String = content
}

internal open class DelegatingMutableSet<From, To>(
    private val delegate: MutableSet<From>,
    private val convertTo: From.() -> To,
    private val convert: To.() -> From
) : MutableSet<To> {

     open fun Collection<To>.convert(): Collection<From> = map { it.convert() }
     open fun Collection<From>.convertTo(): Collection<To> = map { it.convertTo() }

    override val size: Int = delegate.size

    override fun add(element: To): Boolean = delegate.add(element.convert())

    override fun addAll(elements: Collection<To>): Boolean = delegate.addAll(elements.convert())

    override fun clear() {
        delegate.clear()
    }

    override fun remove(element: To): Boolean = delegate.remove(element.convert())

    override fun removeAll(elements: Collection<To>): Boolean = delegate.removeAll(elements.convert())

    override fun retainAll(elements: Collection<To>): Boolean = delegate.retainAll(elements.convert())

    override fun contains(element: To): Boolean = delegate.contains(element.convert())

    override fun containsAll(elements: Collection<To>): Boolean = delegate.containsAll(elements.convert())

    override fun isEmpty(): Boolean = delegate.isEmpty()

    override fun iterator(): MutableIterator<To> = object : MutableIterator<To> {
        val delegateIterator = delegate.iterator()

        override fun hasNext(): Boolean = delegateIterator.hasNext()

        override fun next(): To = delegateIterator.next().convertTo()

        override fun remove() = delegateIterator.remove()
    }

    override fun hashCode(): Int = delegate.hashCode()

    override fun equals(other: Any?): Boolean {
        if (other == null || other !is Set<*>) return false

        val elements = delegate.convertTo()
        return other.containsAll(elements) && elements.containsAll(other)
    }

    override fun toString(): String = delegate.convertTo().toString()
}
